﻿using System.Threading;
using System.Collections;
using System.Net.Util;
namespace System.Net
{
    /// <summary>
    /// RequestTimeoutManager
    /// </summary>
    internal class RequestTimeoutManager
    {
        private int _currentList;
        private int _inProgressLock;
        private DoubleLinkList[] _lists = new DoubleLinkList[13];
        private int _requestCount = 0;
        private Timer _timer;
        private readonly TimeSpan _timerPeriod = new TimeSpan(0, 0, 15);

        internal RequestTimeoutManager()
        {
            for (int index = 0; index < _lists.Length; index++)
                _lists[index] = new DoubleLinkList();
            _currentList = 0;
            _inProgressLock = 0;
            _timer = new Timer(new TimerCallback(TimerCompletionCallback), null, _timerPeriod, _timerPeriod);
        }

        internal void Add(HttpListenerWorkerRequest wr)
        {
            if (wr.TimeoutLink != null)
                ((RequestTimeoutEntry)wr.TimeoutLink).IncrementCount();
            else
            {
                var entry = new RequestTimeoutEntry(wr);
                int index = _currentList++;
                if (index >= _lists.Length)
                {
                    index = 0;
                    _currentList = 0;
                }
                entry.AddToList(_lists[index]);
                Interlocked.Increment(ref _requestCount);
                wr.TimeoutLink = entry;
            }
        }

        private void CancelTimedOutRequests(DateTime now)
        {
            if (Interlocked.CompareExchange(ref _inProgressLock, 1, 0) == 0)
            {
                var list = new ArrayList(_requestCount);
                for (int i = 0; i < _lists.Length; i++)
                {
                    lock (_lists[i])
                    {
                        var enumerator = _lists[i].GetEnumerator();
                        while (enumerator.MoveNext())
                            list.Add(enumerator.GetDoubleLink());
                        enumerator = null;
                    }
                }
                int count = list.Count;
                for (int j = 0; j < count; j++)
                    ((RequestTimeoutEntry)list[j]).TimeoutIfNeeded(now);
                Interlocked.Exchange(ref _inProgressLock, 0);
            }
        }

        internal void Remove(HttpListenerWorkerRequest wr)
        {
            var timeoutLink = (RequestTimeoutEntry)wr.TimeoutLink;
            if (timeoutLink != null)
            {
                if (timeoutLink.DecrementCount() != 0)
                    return;
                timeoutLink.RemoveFromList();
                Interlocked.Decrement(ref _requestCount);
            }
            wr.TimeoutLink = null;
        }

        internal void Stop()
        {
            if (_timer != null)
            {
                _timer.Dispose();
                _timer = null;
            }
            while (_inProgressLock != 0)
                Thread.Sleep(100);
            if (_requestCount > 0)
                CancelTimedOutRequests(DateTime.UtcNow.AddYears(1));
        }

        private void TimerCompletionCallback(object state)
        {
            if (_requestCount > 0)
                CancelTimedOutRequests(DateTime.UtcNow);
        }

        /// <summary>
        /// RequestTimeoutEntry
        /// </summary>
        private class RequestTimeoutEntry : DoubleLink
        {
            private HttpListenerWorkerRequest _wr;
            private int _count;
            private DoubleLinkList _list;

            internal RequestTimeoutEntry(HttpListenerWorkerRequest wr)
            {
                _wr = wr;
                _count = 1;
            }

            internal void AddToList(DoubleLinkList list)
            {
                lock (list)
                {
                    list.InsertTail(this);
                    _list = list;
                }
            }

            internal int DecrementCount() { return Interlocked.Decrement(ref _count); }

            internal void IncrementCount() { Interlocked.Increment(ref _count); }

            internal void RemoveFromList()
            {
                if (_list != null)
                {
                    lock (_list)
                    {
                        base.Remove();
                        _list = null;
                    }
                }
            }

            internal void TimeoutIfNeeded(DateTime now)
            {
                if (!HttpListenerHost.UseThreading)
                {
                    throw new NotImplementedException();
                    //var task = _wr.MustTimeoutTask(now);
                    //if (task != null)
                    //{
                    //    RemoveFromList();
                    //    //task.Abort(new HttpListenerHost.CancelModuleException(true));
                    //}
                    //return;
                }
                var thread = _wr.MustTimeout(now);
                if (thread != null)
                {
                    RemoveFromList();
                    thread.Abort(new HttpListenerHost.CancelModuleException(true));
                }
            }
        }
    }
}
